手把手带你在微前端(qiankun)中实现多页签功能(路由keepalive)

您所在的位置:网站首页 react 配置路由表 手把手带你在微前端(qiankun)中实现多页签功能(路由keepalive)

手把手带你在微前端(qiankun)中实现多页签功能(路由keepalive)

2023-04-28 12:48| 来源: 网络整理| 查看: 265

效果展示

00.gif

前言

在上篇文章中已经实现了基本的多页签功能,接下来在微前端中实现多页签功能。建议没看过上一篇的朋友,可以先看下上一篇。

手把手带你基于ant design pro 5实现多tab页(路由keepalive) juejin.cn/post/722476…

实现思路

实现起来很简单,还记得上篇文章中获取路由组件实例用的是useOutlet这个hooks,本篇我们只需要自己实现一下这个方法就能实现微前端的多页签了。

具体实现 初始化微前端项目 创建pnpm workspace项目

不了解pnpm workspace的可以从网上先了解一下,首先我们先建一个目录,并进入

mkdir antd-pro-qiankun-keepalive-tabs && cd antd-pro-qiankun-keepalive-tabs 复制代码

执行下面命令,初始化项目

pnpm init 复制代码

在根目录新建pnpm-workspace.yaml文件,并写入以下代码

packages: - "modules/*" 复制代码

在根目录下新建modules目录,我们微前端模块放在里面

mkdir modules 复制代码

进入modules文件夹,使用antd pro脚手架初始化微前端主项目,选umi4

cd modules && pro create main 复制代码

在modules文件夹下创建app1文件夹,然后执行以下命令

cd app1 && pnpm create umi 复制代码

选择Simple App

image.png

使用pnpm

image.png 初始化后,会自动安装依赖

我们在app1根目录下创建.env文件,更改启动端口 image.png

再回到项目根目录下,更改package.json文件,增加一个start命令

"start": "pnpm -C ./modules/main start & pnpm -C ./modules/app1 start" 复制代码

image.png 然后执行启动命令

npm start 复制代码

启动成功后,访问http://localhost:8000 和 http://localhost:8001, 此时应该能正常访问。

使用qiankun微前端插件

在主项目modules/main/config/config.ts配置文件中使用qiankun插件,具体qiankun用法请参考官网

qiankun: { master: { apps: [{ name: 'app1', entry: '//localhost:8001' }], } }, 复制代码

image.png

更改app1目录下package.json中name字段,改成app1

image.png

给app1模块安装umi插件,执行以下命令

pnpm --filter "app1" add @umijs/plugins 复制代码

安装完依赖后,在modules/app1/.umirc.ts配置文件中使用qiankun插件

plugins: ['@umijs/plugins/dist/qiankun'], qiankun: { slave: {}, }, 复制代码

image.png 然后再更改主项目modules/main/config/routes.ts路由文件,添加子模块app1路由

{ name: 'app1-index', icon: 'table', path: '/app1/*', microApp: 'app1' }, 复制代码

image.png

回到根目录执行npm start命令,启动项目。启动成功后,访问http:localhost:8000 登录成功后,点击app1-index菜单,就能看到app1模块 src/pages/index.tsx里的内容了。

image.png 到此微前端项目搭建成功了

实现多页签功能

把上一篇文章中src/layouts文件夹和文件一起复制到当前主项目src目录下

image.png 上文中提到我们只需要自己实现useOutlet这个hooks就行了,那么我们就来实现这个hooks,在layouts目录下新建useOutlet.tsx文件

import { MicroAppWithMemoHistory, useLocation } from '@umijs/max' export function useOutlet() { const { pathname } = useLocation(); // 获取模块名 const [, moduleName] = pathname.split('/'); // 加载微前端模块另外一种方式 return ( ) } 复制代码

把路由中的microApp属性移除,因为使用了MicroAppWithMemoHistory这种加载方式,具体请参考qiankun官网。再添加一个app1 docs路由测试,更改重定向路由到/app1/

image.png

修改主项目qiankun配置,关闭微前端单例模式,如果开启单例,就没办法保留其他tab的状态了。把singular设置为false

qiankun: { master: { apps: [{ name: 'app1', entry: '//localhost:8001' }], singular: false, }, }, 复制代码

移除app1模块/src/layouts文件夹,并在路由中移除,移除后有可能会报错,重启一下服务就行了。

更改app1模块中src/pages/index.tsx文件,添加一个input组件,测试切换路由是否能保持状态,并且给div设置一个很大的高度,测试是否能保留滚动条位置。

image.png 效果展示

09.gif

支持主项目路由

现在是不支持主项目路由的,切换到“欢迎”菜单会报错,我们兼容一下主项目路由。

image.png 实现思路:在useOutlet这个hooks中,判断一下路由是否是子模块,如果不是子模块,就使用原始的useOutlet的返回值。

把主项目qiankun配置抽到一个单独的文件中导出,方便我们在代码中获取有哪些子模块。然后再主项目app.ts文件中引入qiankun配置,然后再导出。

删除config文件中的qiankun apps和singular配置 image.png

在主项目src目录下新建qiankun-config.ts文件,代码如下

export const qiankunConfig = { master: { apps: [{ name: 'app1', entry: '//localhost:8001' }], singular: false, }, }; 复制代码

在src/app.ts文件中赋值给qiankun,并导出qiankun。 image.png

更改useOutlet.tsx文件

import { MicroAppWithMemoHistory, useLocation, useOutlet as useChildren } from '@umijs/max' import { qiankunConfig } from '@/qiankun-config'; export function useOutlet() { const { pathname } = useLocation(); const children = useChildren(); const [, moduleName] = pathname.split('/'); // 如果没有匹配到子模块,就使用主模块的useOutlet if (!qiankunConfig.apps.some(app => app.name === moduleName)) { return children; } return ( ) } 复制代码

主模块的路由也支持了

image.png

在子模块中调用刷新、关闭、关闭其他功能,并监听onShow和onHidden事件

实现思路:通过父子模块通信,把这几个方法传到子模块中

改造/src/layouts/indes.tsx文件,把这几个方法传给子模块。

image.png

子模块中使用useModel获取主模块传过来的数据,首先在modules/app1/.umirc.ts中引入@umijs/plugins/dist/model插件

image.png

使用const { keepAliveHandle } = useModel('@@qiankunStateFromMaster');获取主项目传过来的数据,在app1/src/pages/index中测试refreshTab、closeTab、closeOhterTab、onShow、onHidden方法

code.png

image.png

解决bug

我以为上面东西搞完,基本就结束了,谁知道在测试的时候发现了个大bug。bug流程是这样的,a切换到b,关掉a,再打开a就会一直空白。然后报这个错:dex.js:1 single-spa minified message #31: See https://single-spa.js.org/error/?code=31&arg=mount&arg=parcel&arg=app1_1&arg=3000我到qiankun仓库issue找相关解决方案,发现也有人遇到这个问题,但是没有明确的解决方案,然后自己扒qiankun源码看,最后发现了bug的原因。原来qiankun在加载子模块生命周期的时候,会做缓存,缓存的key用的微前端容器的xpath路径,我先打开一个,然后在打开一个,然后关掉第一个,然后再打开第一个,这时候新加的xpath和前面一个一样了,然后qiankun发现当前xpath已经加载了,就不再去加载。这个bug大致是这样触发的,我们只需要把缓存的key改成当前url + xpath就行了,这个我后面会单独出一篇文章去讲,这块逻辑有点复杂。

bug演示

11.gif

然后我就把umi-qiankun插件引入到当前主项目中,改了点源码,后面会给umi提个pr,作者可能不会合并,因为这种使用场景不多。

image.png 改了这里,如果组件传id就用组件传的,如果没传,还用以前的name。

image.png 改造useOutlet hooks,组件id属性设置为pathname

10.gif

添加新模块app2

复制app1整个文件夹内容到app2文件夹下,改.env的启动端口为8002,然后主项目配置一下路由和微前端配置。

image.png

效果展示

12.gif

总结

我这个实现方案应该会有性能问题,最好的方案应该在子模块中实现缓存,同一个模块不用每次都重新加载,性能应该会好一点。大家有更好的方案可以在评论区提醒我一下。

后面我会把这个功能做成umi插件,方便大家使用,接下来应该会出一篇umi插件开发的文章教程,对这个感兴趣的可以关注我一下。

文中的代码我已经上传到github了,地址是:github.com/dbfu/antd-p…



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3